import {guid, isEmpty, isNotEmpty} from '@/utils/CommonUtils';

export default class {
  flowModel;
  formDefinition;
  containerID;
  renderContext = {
    svg: null,
    inner: null,
    render: null,
    zoom: null,
    graph: null
  };
  originTransForm = undefined;

  constructor({flowModel, formDefinition, containerID}) {
    this.flowModel = flowModel;
    this.formDefinition = formDefinition;
    this.containerID = containerID;

    this.bindContainerStyle();
    this.originTransForm = undefined;
  }

  bindContainerStyle() {
    $(`#${this.containerID}`).parent().addClass('flow-model-container');
    $(`#${this.containerID}`).parent().addClass('flow-model-container-editable');
  }

  render() {
    this.resolveContainerStyle();
    return this.renderFlowModel();
  }

  resolveContainerStyle() {

  }

  renderFlowModel() {
    this.drawBackground();
    this.renderContext = this.prepareDraw();
    this.drawNodes(this.renderContext);

    this.drawLines(this.renderContext);

    let {inner, render, graph} = this.renderContext;
    inner.call(render, graph);

    this.resolveInitZoom();
    return new Promise((success, err) => {
      success();
    });
  }

  resolveInitZoom() {
    if (this.originTransForm) {
      this.setZoom(this.originTransForm.x, this.originTransForm.y, this.originTransForm.scale);
    } else {
      this.setZoom(0, 0, 1);
    }
  }

  drawBackground() {
  }

  prepareDraw() {
    this.clearImage();
    let svg = d3.select(`#${this.containerID}`);
    let inner = svg.select("g");
    let graph = this.createGraph();
    let render = new dagreD3.render();
    let zoom = this.createZoom(inner);

    svg.call(zoom);
    return {
      zoom, svg, inner,
      render, graph
    };
  }

  createZoom(inner) {
    let that = this;
    let zoom = d3.zoom().on("zoom", function () {
      inner.attr("transform", d3.event.transform);
      that.originTransForm = {
        x: d3.event.transform.x,
        y: d3.event.transform.y,
        scale: d3.event.transform.k,
      };
    });
    return zoom;
  }

  createGraph() {
    let graph = new dagreD3.graphlib.Graph();
    graph.setGraph({
      rankdir: "LR",
      marginx: 20,
      marginy: 20
    });
    return graph;
  }

  clearImage() {
    $(`#${this.containerID}`).find('g').html('');
    $(`#${this.containerID}`).find('g').attr('transform', '');
  }

  setZoom(x, y, scale) {
    let {inner, zoom, svg} = this.renderContext;
    let transform = d3.zoomIdentity.translate(x, y).scale(scale);
    svg.call(zoom.transform, transform);
  }

  drawNodes({graph}) {
    let node = this.createStartNode();
    this.resolveNodeStyle(node);
    graph.setNode(node.id, node.node);
    node = this.createEndNode();
    this.resolveNodeStyle(node);
    graph.setNode(node.id, node.node);

    let nodes = this.flowModel.nodes;
    if (nodes && nodes.length) {
      for (let i = 0; i < nodes.length; i++) {
        node = this.createNodeGraphByNode(nodes[i]);
        this.resolveNodeStyle(node, nodes[i]);
        if (node) {
          graph.setNode(node.id, node.node);
        }
      }
    }
  }

  resolveNodeStyle(node) {

  }

  drawLines({graph}) {
    this.tryOrderLines();
    let lines = this.flowModel.lines;

    if (isEmpty(lines)) return;
    for (let i = 0; i < lines.length; i++) {
      let label = '';
      if (this.isGateWayStart({id: lines[i].start})) {
        label = this.createAndFormulaButton(lines, i);
      }

      if (isNotEmpty(lines[i].formula)) {
        label = this.getFormulaTitleButton(lines, i);
      }

      let line = {
        labelType: "html",
        label
      };
      this.resolveLineStyle(line, lines[i]);
      graph.setEdge(lines[i].start, lines[i].end, line);
    }
  }


  resolveLineStyle(line, lineDefinition) {
  }

  getFormulaTitleButton(lines, i) {
    return `<span class="line-formula-text line-formula-config-icon"
                    data-line-id="${lines[i].id}">
                    ${lines[i].formula.formulaTitle}
                  </span>`;
  }

  createAndFormulaButton(lines, i) {
    return `<span class="line-formula-text line-formula-config-icon"
                    data-line-id="${lines[i].id}">
                      ${this.getFormulaConfigIcon(lines[i].id)}
                  </span>`;
  }

  getFormulaConfigIcon(id) {
    return `<i class="el-icon" data-line-id="${id}"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" data-line-id="${id}"><path fill="currentColor" d="M352 480h320a32 32 0 1 1 0 64H352a32 32 0 0 1 0-64z"></path><path fill="currentColor" d="M480 672V352a32 32 0 1 1 64 0v320a32 32 0 0 1-64 0z"></path><path fill="currentColor" d="M512 896a384 384 0 1 0 0-768 384 384 0 0 0 0 768zm0 64a448 448 0 1 1 0-896 448 448 0 0 1 0 896z"></path></svg></i>`;
  }

  isGateWayStart(node) {
    let gatewayPairs = this.flowModel.gatewayPairs;
    for (let i = 0; i < gatewayPairs.length; i++) {
      if (gatewayPairs[i].start === node.id) return true;
    }
    return false;
  }

  createNodeGraphByNode(node) {
    if (node && node.type && this['create' + node.type + 'GraphNode']) {
      return this['create' + node.type + 'GraphNode'](node);
    }
  }

  createUserTaskNode() {
    return {
      id: guid(),
      type: 'user',
      name: '用户审批',
      userConfigs: [],
      executeWay: 'single',
      pcViewId: this.formDefinition.views[0].id,
      pcViewActionId: this.getDefaultViewAction(),
      mobileViewId: this.formDefinition.views[0].id,
      mobileViewActionId: this.getDefaultViewAction(),
      executeCompleteThreshold: 100
    };
  }

  getDefaultViewAction() {
    if (isEmpty(this.formDefinition)) return undefined;
    let viewActions = this.formDefinition.viewActions;
    if (isEmpty(viewActions)) return undefined;
    let res = viewActions.find(v => v.systemPreset + '' === '1' && v.actionType === 'approve');
    if (isEmpty(res)) return undefined;
    return res.id;
  }

  createStartNode() {
    return {
      id: 'start',
      node: {
        labelType: "html",
        label: `<div class="label start-label">发起人</div>`,
        padding: 0,
        class: 'start-node'
      }
    };
  }

  updateTask(userTask) {
    if (isEmpty(this.flowModel)) return;
    let nodes = this.flowModel.nodes;
    if (isEmpty(nodes)) nodes = [];
    let index = nodes.findIndex(node => node.id === userTask.id);
    if (index !== -1) {
      nodes[index] = userTask;
    }
    return this.renderFlowModel();
  }

  createEndNode() {
    return {
      id: 'end',
      node: {
        labelType: "html",
        label: '<div class="label end-label">结束</div>',
        padding: 0,
        class: 'end-node'
      }
    };
  }

  insertUserTaskAfter(userTask, taskId) {
    // 增加一个节点
    this.flowModel.nodes.push(userTask);

    // 把start直接指向新的节点， 新的节点直接指向start后面的节点
    let lines = this.flowModel.lines;
    for (let i = 0; i < lines.length; i++) {
      if (lines[i].start === taskId) lines[i].start = userTask.id;
    }

    lines.push(this.createLine(taskId, userTask.id));
    return this.renderFlowModel();
  }

  insertUserTaskBefore(userTask, taskId) {
    // 增加一个节点
    this.flowModel.nodes.push(userTask);

    // 把start直接指向新的节点， 新的节点直接指向start后面的节点
    let lines = this.flowModel.lines;
    for (let i = 0; i < lines.length; i++) {
      if (lines[i].end === taskId) lines[i].end = userTask.id;
    }

    lines.push(this.createLine(userTask.id, taskId));
    return this.renderFlowModel();
  }

  createLine(start, end) {
    return {
      id: guid(),
      start,
      end
    };
  }

  getTaskById(taskId) {
    if (taskId === 'start') return {id: 'start'};
    if (taskId === 'end') return {id: 'end'};
    for (let i = 0; i < this.flowModel.nodes.length; i++) {
      if (this.flowModel.nodes[i].id === taskId) {
        return this.flowModel.nodes[i];
      }
    }
  }

  getBeforeNodes(task) {
    let list = [];
    for (let i = 0; i < this.flowModel.lines.length; i++) {
      if (this.flowModel.lines[i].end === task.id) {
        list.push(this.getTaskById(this.flowModel.lines[i].start));
      }
    }
    return list;
  }

  getAfterNodes(task) {
    let list = [];
    for (let i = 0; i < this.flowModel.lines.length; i++) {
      if (this.flowModel.lines[i].start === task.id) {
        list.push(this.getTaskById(this.flowModel.lines[i].end));
      }
    }
    return list;
  }

  genGatewayPair() {
    let startGatewayId = guid();
    let endGatewayId = guid();
    return {
      startGateway: {
        id: startGatewayId,
        type: 'gateway'
      },
      endGateway: {
        id: endGatewayId,
        type: 'gateway'
      },
      gatewayPair: {
        start: startGatewayId,
        end: endGatewayId
      }
    };
  }

  inCount(task) {
    let count = 0;
    for (let i = 0; i < this.flowModel.lines.length; i++) {
      if (this.flowModel.lines[i].end === task.id) {
        count++;
      }
    }
    return count;
  }

  outCount(task) {
    let count = 0;
    for (let i = 0; i < this.flowModel.lines.length; i++) {
      if (this.flowModel.lines[i].start === task.id) {
        count++;
      }
    }
    return count;
  }

  addParallelNode(userTask, taskId) {
    let clickingTask = this.getTaskById(taskId);
    if (!clickingTask) return;
    // 增加一个节点
    this.flowModel.nodes.push(userTask);

    if (this.inCount(clickingTask) === 1
      && this.outCount(clickingTask) === 1
      && this.isPairGateWay(this.getBeforeNodes(clickingTask)[0], this.getAfterNodes(clickingTask)[0])) {
      // 简单插入即可
      let lines = this.flowModel.lines;
      lines.push(this.createLine(this.getBeforeNodes(clickingTask)[0].id, userTask.id));
      lines.push(this.createLine(userTask.id, this.getAfterNodes(clickingTask)[0].id));
    } else {
      // 需要生成网关然后插入
      let {startGateway, endGateway, gatewayPair} = this.genGatewayPair();
      this.flowModel.nodes.push(startGateway, endGateway);
      this.flowModel.gatewayPairs.push(gatewayPair);

      let lines = this.flowModel.lines;
      for (let i = 0; i < lines.length; i++) {
        if (lines[i].end === clickingTask.id) {
          lines[i].end = startGateway.id;
        }
        if (lines[i].start === clickingTask.id) {
          lines[i].start = endGateway.id;
        }
      }
      lines.push(this.createLine(startGateway.id, clickingTask.id));

      lines.push(this.createLine(startGateway.id, userTask.id));

      lines.push(this.createLine(clickingTask.id, endGateway.id));

      lines.push(this.createLine(userTask.id, endGateway.id));
    }
    return this.renderFlowModel();
  }

  isPairGateWay(gateway1, gateway2) {
    let gatewayPairs = this.flowModel.gatewayPairs;
    for (let i = 0; i < gatewayPairs.length; i++) {
      if (gatewayPairs[i].start === gateway1.id && gatewayPairs[i].end === gateway2.id) {
        return true;
      }
      if (gatewayPairs[i].end === gateway1.id && gatewayPairs[i].start === gateway2.id) {
        return true;
      }
    }
    return false;
  }

  createuserGraphNode(node) {
    return {
      id: node.id,
      node: {
        labelType: "html",
        label: `<div class="label user-label" data-user-task="${node.id}" title="${node.name}">${node.name}</div>`,
        padding: 0,
        class: 'user-node'
      }
    };
  }

  createNodeGraphByNode(node) {
    if (node && node.type && this['create' + node.type + 'GraphNode']) {
      return this['create' + node.type + 'GraphNode'](node);
    }
  }

  createemptyGraphNode(node) {
    return {
      id: node.id,
      node: {
        labelType: "html",
        label: `<div class="label empty-label" data-empty-task="${node.id}"></div>`,
        padding: 0,
        class: 'user-node'
      }
    };
  }

  creategatewayGraphNode(node) {
    let clazz = this.getGateWayClass(node);

    return {
      id: node.id,
      node: {
        labelType: "html",
        label: `<div data-gateway="${node.id}" class="label gateway-label ${clazz}"></div>`,
        padding: 0,
        class: `gateway-node ${clazz}`
      }
    };
  }

  getGateWayClass(node) {
    if (this.isGateWayStart(node)) {
      return 'gateway-label-start';
    }
    return 'gateway-label-end';
  }

  addLineByNode(start, end) {
    this.flowModel.lines.push({
      id: guid(),
      start: start.id,
      end: end.id,
    });
  }

  getBeforeLineByNodeId(id) {
    let temp = [], lines = this.flowModel.lines;
    for (let i = 0; i < lines.length; i++) {
      if (id === lines[i].end) {
        temp.push(lines[i]);
      }
    }
    return temp;
  }

  getAfterLineByNodeId(id) {
    let temp = [], lines = this.flowModel.lines;
    for (let i = 0; i < lines.length; i++) {
      if (id === lines[i].start) {
        temp.push(lines[i]);
      }
    }
    return temp;
  }

  addEmptyNode(clickingNode) {
    let task = this.getTaskById(clickingNode);
    let before = this.getBeforeNodes(task)[0];
    let after = this.getAfterNodes(task)[0];
    let node = {
      id: guid(),
      type: 'empty',
      name: ''
    };
    this.flowModel.nodes.push(node);
    if (this.isPairGateWay(before, after)) {
      this.addLineByNode(before, node);
      this.addLineByNode(node, after);
    } else {
      let {startGateway, endGateway, gatewayPair} = this.genGatewayPair();
      this.flowModel.nodes.push(startGateway, endGateway);
      this.flowModel.gatewayPairs.push(gatewayPair);

      this.getBeforeLineByNodeId(task.id)[0].end = startGateway.id;
      this.getAfterLineByNodeId(task.id)[0].start = endGateway.id;

      this.addLineByNode(startGateway, node);
      this.addLineByNode(node, endGateway);

      this.addLineByNode(startGateway, task);
      this.addLineByNode(task, endGateway);
    }

    return this.renderFlowModel();
  }

  removeSelectedNode(taskId) {
    let clickingTask = this.getTaskById(taskId);
    if (this.inCount(clickingTask) === 1
      && this.outCount(clickingTask) === 1
      && this.isPairGateWay(this.getBeforeNodes(clickingTask)[0], this.getAfterNodes(clickingTask)[0])) {
      // 所有相关的线直接删除即可
      this.flowModel.lines = this.flowModel.lines.filter(line => {
        return !(line.start === taskId || line.end === taskId);
      });
    } else if (this.inCount(clickingTask) === 1 && this.outCount(clickingTask) === 1) {
      this.flowModel.lines.push({
        id: guid(),
        start: this.getBeforeNodes(clickingTask)[0].id,
        end: this.getAfterNodes(clickingTask)[0].id
      });
      this.flowModel.lines = this.flowModel.lines.filter(line => {
        return !(line.start === taskId || line.end === taskId);
      });
    }

    this.flowModel.nodes = this.flowModel.nodes.filter(line => {
      return !(line.id === taskId);
    });

    while (this.removeRedundanceGateWays()) ;
    return this.renderFlowModel();
  }

  removeRedundanceGateWays() {
    let flag = false;
    let gatewayPairs = this.flowModel.gatewayPairs;
    for (let i = 0; i < gatewayPairs.length; i++) {
      let startGateWay = this.getTaskById(gatewayPairs[i].start);
      let endGateway = this.getTaskById(gatewayPairs[i].end);
      if (this.outCount(startGateWay) === 1) {
        flag = true;
        gatewayPairs.splice(i--, 1);

        let newLineStart = this.getBeforeNodes(startGateWay)[0].id;
        let newLineEnd = this.getAfterNodes(startGateWay)[0].id;
        this.flowModel.lines = this.flowModel.lines.filter(line => {
          return !(line.start === startGateWay.id || line.end === startGateWay.id);
        });
        this.flowModel.nodes = this.flowModel.nodes.filter(line => {
          return !(line.id === startGateWay.id);
        });
        this.flowModel.lines.push({
          id: guid(),
          start: newLineStart,
          end: newLineEnd,
        });

        newLineStart = this.getBeforeNodes(endGateway)[0].id;
        newLineEnd = this.getAfterNodes(endGateway)[0].id;
        this.flowModel.lines = this.flowModel.lines.filter(line => {
          return !(line.start === endGateway.id || line.end === endGateway.id);
        });
        this.flowModel.nodes = this.flowModel.nodes.filter(line => {
          return !(line.id === endGateway.id);
        });
        this.flowModel.lines.push({
          id: guid(),
          start: newLineStart,
          end: newLineEnd,
        });
      }
    }
    return flag;
  }

  getLineByElement(target) {
    let id = undefined;
    while (target && !id) {
      id = target.getAttribute('data-line-id');
      target = target.parentNode;
    }

    if (isEmpty(id)) {
      throw new Error('lineId是空的');
    }
    return this.getLineById(id);
  }

  getLineById(lineId) {
    let lines = this.flowModel.lines;
    for (let i = 0; i < lines.length; i++) {
      if (lineId === lines[i].id) {
        return lines[i];
      }
    }
    return null;
  }

  simpleAddNode(userTask) {
    this.flowModel.nodes = this.flowModel.nodes.filter(n => n.id !== userTask.id);
    this.flowModel.nodes.push(userTask);
    return this.renderFlowModel();
  }

  tryOrderLines() {
    try {
      let res = [];
      this.doTryOrderLines(res, 'start');
      this.flowModel.lines = res;
    } catch (e) {
      console.error(e);
    }
  }

  doTryOrderLines(res, nodeId) {
    let lines = this.flowModel.lines.filter(l => l.start === nodeId && isEmpty(res.find(r => r.id === l.id)));
    if (isEmpty(lines)) return;
    for (let i = 0; i < lines.length; i++) {
      res.push(lines[i]);
      this.doTryOrderLines(res, lines[i].end);
    }
  }
};
